/*
 * Created on 2004-12-1
 */
package org.nanhill.commons.lang;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.zip.DataFormatException;

/**
 * @author wanglin
 */
public abstract class DateKit {
	public final static long MILLIS_ONE_DAY = 24 * 3600 * 1000;

	/**
	 * The year of the gregorianCutover, with 0 representing 1 BC, -1
	 * representing 2 BC, etc.
	 */
	private final static int gregorianCutoverYear = 1582;

	/**
	 * Notice: month start from index 1
	 * 
	 * days of every month except leap year
	 */
	private final static int[] MAX_DAYS_OF_MONTHS_EXCEPT_LEAP_YEAR = { 0, 31,
			28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

	/**
	 * Determines if the given year, month, and date is logical
	 * 
	 * @param year
	 *            0~
	 * @param month
	 *            1~12
	 * @param date
	 *            1~31
	 * @return
	 */
	public static boolean isLogical(int year, int month, int date) {
		if (year < 0 || month < 1 || month > 12 || date < 1) {
			return false;
		}
		// if the year is leap year
		if (isLeapYear(year)) {
			return month == 2 && date > 29 ? false : true;
		} else {
			return date > MAX_DAYS_OF_MONTHS_EXCEPT_LEAP_YEAR[month] ? false
					: true;
		}
	}

	/**
	 * null safe
	 * 
	 * @param year
	 * @param month
	 * @param date
	 * @return
	 */
	public static boolean isLogical(String year, String month, String date) {
		int yearInt, monthInt, dateInt;
		try {
			yearInt = Integer.parseInt(year);
			monthInt = Integer.parseInt(month);
			dateInt = Integer.parseInt(date);
		} catch (NumberFormatException e) {
			return false;
		}
		return isLogical(yearInt, monthInt, dateInt);
	}

	/**
	 * 
	 * @param year
	 * @param month
	 * @param date
	 * @return not null only the date is exists
	 */
	public static long getTimeInMillis(String year, String month, String date)
			throws DataFormatException {
		int yearInt, monthInt, dateInt;
		try {
			yearInt = Integer.parseInt(year);
			monthInt = Integer.parseInt(month);
			dateInt = Integer.parseInt(date);
		} catch (NumberFormatException e) {
			throw new DataFormatException(e.getMessage());
		}
		if (isLogical(yearInt, monthInt, dateInt)) {
			// notice month - 1
			return new GregorianCalendar(yearInt, monthInt - 1, dateInt)
					.getTimeInMillis();
		} else {
			throw new DataFormatException("The date: " + year + "-" + month
					+ "-" + date + " is not exists.");
		}
	}

	public static long getTimeInMillis(int year, int month, int date) {
		return new GregorianCalendar(year, month - 1, date).getTimeInMillis();
	}

	/**
	 * Determines if the given year is a leap year. Returns true if the given
	 * year is a leap year.
	 * 
	 * @param year
	 *            the given year.
	 * @return true if the given year is a leap year; false otherwise.
	 */
	public static boolean isLeapYear(int year) {
		// TODO may be incorrect
		return year >= gregorianCutoverYear ? ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)))
				: // Gregorian
				(year % 4 == 0); // Julian
	}

	public static String formatDate(long time, String style) {
		Date date = new Date(time);
		SimpleDateFormat bartDateFormat = new SimpleDateFormat(style);
		return bartDateFormat.format(date);
	}

	private static final DateFormat df = new SimpleDateFormat(
			"yyyy-MM-dd HH:mm");

	public static String defaultFormat(Date date) {
		return df.format(date);
	}

	// public static String format (Date date, String pattern) {
	// DateFormat df = new SimpleDateFormat(pattern);
	// return df.format(date);
	// }

	public static Date defaultFormat(String date) throws ParseException {
		return df.parse(date);
	}

	public static Date getDate(int year, int month, int day, int hour, int min) {
		return new GregorianCalendar(year, month - 1, day, hour, min).getTime();
	}

	public static Date getDate(int year, int month, int day) {
		return getDate(year, month, day, 0, 0);
	}

	public static Date getAddMonthsDate(Date date, int month) {
		Calendar cl = new GregorianCalendar();
		cl.setTime(date);
		cl.add(Calendar.MONTH, month);
		return cl.getTime();
	}

	public static Date getAddYearsDate(Date date, int year) {
		Calendar cl = new GregorianCalendar();
		cl.setTime(date);
		cl.add(Calendar.YEAR, year);
		return cl.getTime();
	}

	public static Date getAddDaysDate(Date date, int da) {
		Calendar cl = new GregorianCalendar();
		cl.setTime(date);
		cl.add(Calendar.DATE, da);
		return cl.getTime();
	}

	public static Date getAddHoursDate(Date date, int da) {
		Calendar cl = new GregorianCalendar();
		cl.setTime(date);
		cl.add(Calendar.HOUR, da);
		return cl.getTime();
	}

	public static Date getZeroDate(Date date) {
		Calendar cl = new GregorianCalendar();
		cl.setTime(date);
		return getDate(cl.get(Calendar.YEAR), cl.get(Calendar.MONTH) + 1, cl
				.get(Calendar.DATE));

	}

	public static long getIntervalSecond(long time1, long time2) {
		long seconds = (time1 - time2) / 1000;
		return seconds < 0 ? -seconds : seconds;
	}

	public static long getIntervalMinute(long time1, long time2) {
		long minutes = (time1 - time2) / 60000;
		return minutes < 0 ? -minutes : minutes;
	}

	public static long getIntervalHour(long time1, long time2) {
		long hours = (time1 - time2) / 3600000;
		return hours < 0 ? -hours : hours;
	}

	public static long getIntervalDay(long time1, long time2) {
		long days = (time1 - time2) / 86400000;
		return days < 0 ? -days : days;
	}

}